package when3meet.view;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

import javax.swing.JPanel;

import when3meet.model.Edge;

/**
 * TODO adjust width for various number of choice
 * 
 * @author Xi
 * 
 */
public class DecisionLinePanel extends JPanel implements MouseMotionListener {

	int Max;
	Rectangle[] globalEdges;
	Rectangle[] localEdges;
	Rectangle[] choices;
	int[] edgesPosition;
	int currentAllowedEdge = 0;
	int currentEdgeIndex = -1;
	int width, height;

	// judge whether allowed draw new edge at a time
	boolean turn;

	// result points for poly line
	public int[] x;
	int[] y;

	String[] choiceNames;
	static DecisionLinePanel decisionLinePanel;

	/**
	 * default constructor could used for asynchronous, when get response from
	 * server, constructor will set the choices and maximum edges allowed
	 * 
	 * @param maxEdges
	 * @param s
	 */
	private DecisionLinePanel(int maxEdges, String[] s) {

		if (s != null) {
			choiceNames = new String[s.length];

			for (int i = 0; i < s.length; i++) {
				choiceNames[i] = s[i];
			}
			choices = new Rectangle[choiceNames.length];
			width = 810 / (choices.length - 1);
			if (choices.length == 1)
				width = 0;
		}
		Max = maxEdges;
		localEdges = new Rectangle[Max];

		// default width and height

		height = 491;

		// bind mouse action, click to add; double-click to delete
		addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent evt) {
				int x = evt.getX();
				int y = evt.getY();
				addEdge(x, y);
			}

			// delete edge is really non-functional requirement

			// public void mouseClicked(MouseEvent evt) {
			//
			// if (evt.getClickCount() == 2) {
			// remove(currentEdgeIndex);
			// }
			// }
		});
		// move edge also should be non-functional requirement
		// addMouseMotionListener(this);

	}

	/**
	 * default paint method
	 */
	public void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.setColor(Color.PINK);
		// paint edges passed by XML response
		if (globalEdges != null)
			for (int i = 0; i < globalEdges.length; i++)
				((Graphics2D) g).fill(globalEdges[i]);

		// paint edge add GUI action
		if (localEdges != null)
			if (currentEdgeIndex == 0)
				for (int i = 0; i < currentAllowedEdge; i++) {
					if (localEdges[i] == null)
						break;
					((Graphics2D) g).fill(localEdges[i]);
				}

		// paint method when there is conflict among edges
		if (currentEdgeIndex == -1) {
			for (int i = 0; i < currentAllowedEdge - 1; i++)
				((Graphics2D) g).fill(localEdges[i]);
			currentEdgeIndex = 0;
		}

		// paint choice
		g.setColor(Color.WHITE);
		if (choices != null) {

			for (int j = 0; j < choices.length; j++) {

				if (j == choices.length - 1 && choices.length != 1) {

					String s = choiceNames[j];
					// Subtract some value to display full choice name

					int compansateLength = (s.length()) * 2;

					if (s.length() < 5)
						compansateLength = 0;

					((Graphics2D) g).drawString(choiceNames[j], (width * j) - compansateLength + 5, 20);
					((Graphics2D) g).fill(choices[j]);
				}

				else {
					((Graphics2D) g).fill(choices[j]);
					((Graphics2D) g).drawString(choiceNames[j], (width * j) + 11, 20);
				}
			}
		}

		// paint result
		if (x != null) {
			g.setColor(new Color(100, 230, 67));
			float dash[] = { 10.0f };
			((Graphics2D) g).setStroke(new BasicStroke(3.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash,
					0.0f));
			((Graphics2D) g).drawPolyline(x, y, x.length);

			int i;
			for (i = 0; i < choiceNames.length; i++)
				if (choices[i].x == (x[x.length - 1] - 4))
					break;

			if (i == choices.length - 1) {
				String s = choiceNames[i];
				int compansateLength = (s.length()) * 2;

				if (s.length() < 5)
					compansateLength = 0;

				((Graphics2D) g).drawString(choiceNames[i], (width * i) - compansateLength + 5, 20);
				MainFrame.getInstance().getAddEdgeError().setText("So the final choice is: " + choiceNames[i]);
				MainFrame.getInstance().getAddEdgeError().setVisible(true);
			} else {
				((Graphics2D) g).drawString(choiceNames[i], (width * i) + 11, 20);
				MainFrame.getInstance().getAddEdgeError().setText("So the final choice is: " + choiceNames[i]);
				MainFrame.getInstance().getAddEdgeError().setVisible(true);
			}
		}
	}

	/**
	 * add edge to point(x,y) when mouse clicked
	 * 
	 * @param x
	 * @param y
	 */
	public void addEdge(int x, int y) {
		boolean b = true;

		if (turn != true)
			MainFrame.getInstance().getErrorLabel().setVisible(true);
		else if (currentAllowedEdge <= Max && turn == true) {
			// calculate x position, which should stick to choices

			for (int i = 0; i < choiceNames.length - 1; i++) {
				int temp = (width * i) + 19;
				if ((x - temp) < width) {
					if (y < choices[0].y || y > choices[0].y + height) {
						// display error message
						MainFrame.getInstance().getAddEdgeError().setVisible(true);
					} else {
						// cancel error message
						MainFrame.getInstance().getAddEdgeError().setVisible(false);

						// set edge with correct x position
						x = temp;

						// only one edge could be added once

						MainFrame.getInstance().getErrorLabel().setVisible(false);
						localEdges[currentAllowedEdge - 1] = new Rectangle(x, y, width - 8, 5);
						// get current edge, check collision
						b = checkCollision(localEdges[currentAllowedEdge - 1]);
						break;
					}

				}
			}

		}

		// currentEdgeIndex = currentAllowedEdge;
		// currentAllowedEdge++;

		if (b == false) {
			repaint();
		}
	}

	/**
	 * add new choice
	 */
	public void addChoice() {
		for (int i = 0; i < choices.length; i++)

			choices[i] = new Rectangle((width * i) + 11, 30, 8, height);

	}

	/**
	 * paint method for double-click action, delete the edge
	 */
	public void remove(int n) {
		if (n < 0 || n >= currentAllowedEdge)
			return;

		localEdges[n] = null;
		// set currentEdgeIndex to -1 as a flag- when repaint local edges, omit
		// localEdges[currentAllowedEdge], which is null
		currentEdgeIndex = -1;

		repaint();
	}

	/**
	 * <h1>Non-Functional requirement</h1>
	 * 
	 * change mouse type when it's on edges
	 */
	public void mouseMoved(MouseEvent evt) {
		// int x = evt.getX();
		// int y = evt.getY();
		//
		// if (getEdge(x, y) >= 0)
		// setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		// else
		// setCursor(Cursor.getDefaultCursor());

	}

	/**
	 * <h1>non-functional requirement</h1>
	 * 
	 * actions when edges are dragged
	 */
	public void mouseDragged(MouseEvent evt) {
		// int x = evt.getX();
		// int y = evt.getY();
		//
		// if (currentEdgeIndex >= 0) {
		//
		// for (int i = 0; i < choiceNames.length; i++) {
		// int temp = (width * i) + 19;
		//
		// if (x - temp < width) {
		// x = temp;
		// break;
		// }
		// }
		//
		// // if (getEdge(x, y) >= 0)
		// // y = y - 16;
		//
		// Graphics g = getGraphics();
		// g.setColor(Color.PINK);
		// g.setXORMode(getBackground());
		// ((Graphics2D) g).fill(globalEdges[currentEdgeIndex]);
		// globalEdges[currentEdgeIndex].x = x;
		// globalEdges[currentEdgeIndex].y = y;
		//
		// // check collision before add the edge
		// boolean b = checkCollision(globalEdges[currentEdgeIndex]);
		//
		// if (b == false)
		// ((Graphics2D) g).fill(globalEdges[currentEdgeIndex]);
		// g.dispose();
		//
		// }
	}

	/**
	 * when add new edge, current edge index is the newest edge's index. when
	 * drag a edge, current edge index will be the dragged edge
	 * 
	 * copy the array of edge[] exclude the edge with currentEdgeIndex, then
	 * compare currentIndex edge with edges in this array if there is collision,
	 * remove(better to change?) the current edge
	 * 
	 */
	public boolean checkCollision(Rectangle edge) {

		// compare current edge with all edges
		//
		// collision happens iff
		// |edge1.x - edge2.x| <= width(width may change when window is
		// resized!) &&
		// |edge1.y -edge2.y| <= 10(edge's height is 5, we double the value for
		// better visual effect)

		int xDistance, yDistance;

		if (globalEdges != null)
			for (int i = 0; i < globalEdges.length; i++) {
				Rectangle rec = globalEdges[i];
				xDistance = edge.x - rec.x;
				xDistance = java.lang.Math.abs(xDistance);
				yDistance = edge.y - rec.y;
				yDistance = java.lang.Math.abs(yDistance);

				// System.out.println("x distance: " + xDistance +
				// " y distance: "
				// + yDistance);

				// System.out.println(edges[currentEdgeIndex].width);

				if (xDistance <= edge.width + 16 && yDistance < 11) {
					remove(currentAllowedEdge);
					return true;
				}

			}

		// System.out.println("checkCollision(): edges[] size: " + edgeCount
		// + " temp[] size: " + temp.size());
		return false;
	}

	/**
	 * 
	 * @return last added edge
	 */
	public Rectangle getEdge() {
		return localEdges[currentAllowedEdge - 1];
	}

	public int getMax() {
		return Max;
	}

	public void setChoices(String[] temp) {
		this.choiceNames = temp;
		this.choices = new Rectangle[choiceNames.length];

		if (choices.length <= 1)
			width = 0;
		else
			width = 810 / (choices.length - 1);

		addChoice();
		repaint();
	}

	public static DecisionLinePanel getInstance(int i, String[] s) {
		if (decisionLinePanel == null) {
			decisionLinePanel = new DecisionLinePanel(i, s);
		}
		return decisionLinePanel;
	}

	public void addGlobalEdges(Edge e) {
		Rectangle rec = new Rectangle(e.left, e.height, width - 8, 5);

		if (globalEdges == null) {
			globalEdges = new Rectangle[1];
			globalEdges[0] = rec;
		} else {

			Rectangle[] temp = globalEdges;
			globalEdges = new Rectangle[temp.length + 1];

			for (int i = 0; i < temp.length; i++)
				globalEdges[i] = temp[i];

			globalEdges[globalEdges.length - 1] = rec;
		}

		repaint();

	}

	public int getCurrentAllowedEdge() {
		return currentAllowedEdge;
	}

	public void setCurrentAllowedEdge(int i) {

		currentAllowedEdge = i;
		turn = true;
	}

	public void setWait() {
		turn = false;
	}

	public void setEdges(Rectangle[] edges) {
		localEdges = edges;
	}

	public void dispose() {
		decisionLinePanel = null;
		currentAllowedEdge = 0;
	}

	public int getCurrentWidth() {
		return this.width;
	}

	public void setCurrentwidth(int i) {
		width = i;
	}

	public void setPolyLine(Integer[] integers, Integer[] integers2) {

		this.x = new int[integers.length];
		this.y = new int[integers.length];

		for (int i = 0; i < integers.length; i++) {
			this.x[i] = integers[i] - 4;
			this.y[i] = integers2[i];
		}

		repaint();
	}

	public Rectangle[] getChoices() {
		return choices;
	}

	public void setLocalEdges(Rectangle[] e) {
		localEdges = e;
	}
}
